fix(hooks): find formatter configs in parent dirs#364
fix(hooks): find formatter configs in parent dirs#364tsubasakong wants to merge 2 commits intoaffaan-m:mainfrom
Conversation
📝 WalkthroughWalkthroughThe post-edit-format hook now discovers formatter configurations through marker-based detection by walking parent directories, supporting multiple Biome and Prettier config file variants instead of relying solely on Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/hooks/post-edit-format.js (1)
39-64:⚠️ Potential issue | 🟠 MajorKeep walking past
package.json-only directories.Line 55 returns on the first marker, so a nested
package.jsonstill masks a shared root.prettierrc/biome.json. In a workspace likepackages/foo/package.jsonplus a repo-root formatter config,detectFormatter()only inspectspackages/fooand formatting is skipped.🐛 Suggested fix
-const PROJECT_ROOT_MARKERS = ['package.json', ...BIOME_CONFIGS, ...PRETTIER_CONFIGS]; +const PROJECT_CONFIG_MARKERS = [...BIOME_CONFIGS, ...PRETTIER_CONFIGS]; function findProjectRoot(startDir) { let dir = startDir; + let packageRoot = null; while (true) { - if (PROJECT_ROOT_MARKERS.some(marker => fs.existsSync(path.join(dir, marker)))) { + if (PROJECT_CONFIG_MARKERS.some(marker => fs.existsSync(path.join(dir, marker)))) { return dir; } + + if (!packageRoot && fs.existsSync(path.join(dir, 'package.json'))) { + packageRoot = dir; + } const parentDir = path.dirname(dir); if (parentDir === dir) break; dir = parentDir; } - return startDir; + return packageRoot || startDir; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/hooks/post-edit-format.js` around lines 39 - 64, findProjectRoot currently returns immediately when it sees any PROJECT_ROOT_MARKERS (including package.json), which causes nested package.json dirs to mask a repo-level formatter config; change findProjectRoot so it prefers formatter configs (PRETTIER_CONFIGS or BIOME_CONFIGS) over package.json: while walking up, if you find any PRETTIER_CONFIGS or BIOME_CONFIGS file return that dir; if you find package.json record it as a fallback but continue walking; after reaching filesystem root return the recorded package.json dir if any, otherwise return startDir. Update the logic in findProjectRoot and reference PROJECT_ROOT_MARKERS, PRETTIER_CONFIGS, BIOME_CONFIGS, and the findProjectRoot function name when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/hooks/hooks.test.js`:
- Around line 716-726: The test only writes a Unix `npx` shim
(fs.writeFileSync(path.join(binDir, 'npx'), ...)) but the hook script
(scripts/hooks/post-edit-format.js) resolves `npx.cmd` on Windows; update the
test to create the Windows shim as well (e.g., write a corresponding `npx.cmd`
file in binDir that writes the same logFile) or write both `npx` and `npx.cmd`
unconditionally so runScript and post-edit-format.js find the shim on all
platforms; modify the fs.writeFileSync calls that create the shim(s) in the test
near runScript to include the Windows variant (ensure executable/mode behavior
is handled appropriately).
---
Outside diff comments:
In `@scripts/hooks/post-edit-format.js`:
- Around line 39-64: findProjectRoot currently returns immediately when it sees
any PROJECT_ROOT_MARKERS (including package.json), which causes nested
package.json dirs to mask a repo-level formatter config; change findProjectRoot
so it prefers formatter configs (PRETTIER_CONFIGS or BIOME_CONFIGS) over
package.json: while walking up, if you find any PRETTIER_CONFIGS or
BIOME_CONFIGS file return that dir; if you find package.json record it as a
fallback but continue walking; after reaching filesystem root return the
recorded package.json dir if any, otherwise return startDir. Update the logic in
findProjectRoot and reference PROJECT_ROOT_MARKERS, PRETTIER_CONFIGS,
BIOME_CONFIGS, and the findProjectRoot function name when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4fce34e9-c905-4a02-884e-9fa98d6c312b
📒 Files selected for processing (2)
scripts/hooks/post-edit-format.jstests/hooks/hooks.test.js
| fs.writeFileSync( | ||
| path.join(binDir, 'npx'), | ||
| `#!/usr/bin/env node\nconst fs = require('fs');\nfs.writeFileSync(${JSON.stringify(logFile)}, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n`, | ||
| { mode: 0o755 } | ||
| ); | ||
|
|
||
| try { | ||
| const stdinJson = JSON.stringify({ tool_input: { file_path: targetFile } }); | ||
| const result = await runScript(path.join(scriptsDir, 'post-edit-format.js'), stdinJson, { | ||
| PATH: `${binDir}${path.delimiter}${process.env.PATH || ''}`, | ||
| }); |
There was a problem hiding this comment.
Make the formatter shim work on Windows too.
Lines 716-726 only create an npx stub, but scripts/hooks/post-edit-format.js resolves npx.cmd on Windows. The hook will miss this shim there, so the new assertion on logFile becomes platform-dependent.
🪟 Suggested fix
+ const shimJs = path.join(binDir, 'npx-shim.js');
+ fs.writeFileSync(
+ shimJs,
+ `const fs = require('fs');\nfs.writeFileSync(${JSON.stringify(logFile)}, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n`
+ );
fs.writeFileSync(
path.join(binDir, 'npx'),
- `#!/usr/bin/env node\nconst fs = require('fs');\nfs.writeFileSync(${JSON.stringify(logFile)}, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n`,
+ `#!/usr/bin/env node\nrequire(${JSON.stringify(shimJs)});\n`,
{ mode: 0o755 }
);
+ fs.writeFileSync(
+ path.join(binDir, 'npx.cmd'),
+ `@echo off\r\nnode "%~dp0\\npx-shim.js" %*\r\n`,
+ { mode: 0o755 }
+ );As per coding guidelines, "Ensure cross-platform compatibility for Windows, macOS, and Linux via Node.js scripts".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fs.writeFileSync( | |
| path.join(binDir, 'npx'), | |
| `#!/usr/bin/env node\nconst fs = require('fs');\nfs.writeFileSync(${JSON.stringify(logFile)}, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n`, | |
| { mode: 0o755 } | |
| ); | |
| try { | |
| const stdinJson = JSON.stringify({ tool_input: { file_path: targetFile } }); | |
| const result = await runScript(path.join(scriptsDir, 'post-edit-format.js'), stdinJson, { | |
| PATH: `${binDir}${path.delimiter}${process.env.PATH || ''}`, | |
| }); | |
| const shimJs = path.join(binDir, 'npx-shim.js'); | |
| fs.writeFileSync( | |
| shimJs, | |
| `const fs = require('fs');\nfs.writeFileSync(${JSON.stringify(logFile)}, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n` | |
| ); | |
| fs.writeFileSync( | |
| path.join(binDir, 'npx'), | |
| `#!/usr/bin/env node\nrequire(${JSON.stringify(shimJs)});\n`, | |
| { mode: 0o755 } | |
| ); | |
| fs.writeFileSync( | |
| path.join(binDir, 'npx.cmd'), | |
| `@echo off\r\nnode "%~dp0\\npx-shim.js" %*\r\n`, | |
| { mode: 0o755 } | |
| ); | |
| try { | |
| const stdinJson = JSON.stringify({ tool_input: { file_path: targetFile } }); | |
| const result = await runScript(path.join(scriptsDir, 'post-edit-format.js'), stdinJson, { | |
| PATH: `${binDir}${path.delimiter}${process.env.PATH || ''}`, | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/hooks/hooks.test.js` around lines 716 - 726, The test only writes a
Unix `npx` shim (fs.writeFileSync(path.join(binDir, 'npx'), ...)) but the hook
script (scripts/hooks/post-edit-format.js) resolves `npx.cmd` on Windows; update
the test to create the Windows shim as well (e.g., write a corresponding
`npx.cmd` file in binDir that writes the same logFile) or write both `npx` and
`npx.cmd` unconditionally so runScript and post-edit-format.js find the shim on
all platforms; modify the fs.writeFileSync calls that create the shim(s) in the
test near runScript to include the Windows variant (ensure executable/mode
behavior is handled appropriately).
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
|
Opened #371 to carry the currently-validated maintainer path for this fix set. It includes equivalent coverage here, folds in related portability/docs fixes, and has already passed local \
Validated 16 agent files ━━━ Running lib/utils.test.js ━━━ === Testing utils.js === Platform Detection: Directory Functions: Date/Time Functions: Project Name Functions: Session ID Functions: File Operations: findFiles: Edge Cases: System Functions: output() and log(): isGitRepo(): getGitModifiedFiles(): getLearnedSkillsDir(): replaceInFile (behavior): writeFile (edge cases): findFiles (regex chars): readStdinJson(): grepFile (global regex fix): commandExists Edge Cases: findFiles Edge Cases: ensureDir Edge Cases: runCommand Edge Cases: getGitModifiedFiles Edge Cases: replaceInFile Edge Cases: readStdinJson Edge Cases: readStdinJson maxSize truncation: ensureDir Error Propagation (Round 31): runCommand failure output (Round 31): runCommand Security (allowlist + metacharacters): getGitModifiedFiles empty patterns (Round 31): readStdinJson error event (Round 33): getGitModifiedFiles all-invalid patterns (Round 69): Round 71: findFiles (unreadable subdirectory in recursive scan): Round 79: countInFile (valid string pattern): Round 79: grepFile (valid string pattern): Round 84: findFiles (inner statSync catch — broken symlink): getSessionIdShort fallback (Round 85): Round 88: replaceInFile with empty replacement string (deletion): Round 88: countInFile with existing file but non-matching pattern: Round 92: countInFile (non-string non-RegExp pattern): Round 93: countInFile (case-insensitive RegExp, g flag auto-appended): Round 93: countInFile (case-insensitive RegExp, g flag preserved): Round 95: countInFile (regex alternation without g flag): Round 97: getSessionIdShort (whitespace-only session ID): Round 97: countInFile (RegExp lastIndex reuse validation): Round 98: findFiles (maxAge: -1 — negative boundary excludes all): Round 99: replaceInFile (no-match still returns true): Round 99: grepFile (CR-only line endings — classic Mac format): Round 100: findFiles (maxAge + recursive combined — untested interaction): Round 101: output() (circular reference — JSON.stringify crash): Round 103: countInFile (boolean false — explicit type guard returns 0): Round 103: grepFile (numeric 0 — implicit toString via RegExp constructor): Round 105: grepFile (sticky y flag — not stripped like g, stateful .test() bug): Round 107: grepFile (empty line matching — ^$ on split lines, trailing \n creates extra empty element): Round 107: replaceInFile (replacement contains search pattern — String.replace is single-pass): Round 106: countInFile (named capture groups — String.match(g) returns full matches only): Round 106: grepFile (multiline m flag — preserved in regex, unlike g which is stripped): Round 109: appendFile (new file creation — ensureDir creates parent, appendFileSync creates file): Round 108: grepFile (Unicode/emoji — regex matching on UTF-16 split lines): Round 110: findFiles (root directory unreadable — EACCES on readdirSync caught silently): Round 113: replaceInFile (zero-width regex /(?:)/g — matches every position): Round 114: replaceInFile (options.all silently ignored for RegExp search): Round 114: output (object containing BigInt — JSON.stringify throws): Round 115: countInFile (empty string pattern — matches at every zero-width position): Round 117: grepFile (CRLF content — trailing \r breaks anchored regex patterns): Round 116: replaceInFile (null/undefined replacement — JS coerces to string "null"/"undefined"): Round 116: ensureDir (null path — fs.existsSync(null) throws TypeError): Round 118: writeFile (non-string content — TypeError propagates uncaught): Round 119: appendFile (non-string content — TypeError propagates like writeFile): Round 120: replaceInFile (empty string search — replace vs replaceAll dramatic difference): Round 121: findFiles (? glob pattern — converted to . regex for single char match): Round 122: findFiles (dot escaping — *.txt matches file.txt but not filetxt): Round 123: countInFile (overlapping patterns — String.match(/g/) is non-overlapping): Round 123: replaceInFile ( Round 124: findFiles (* glob matches dotfiles — unlike shell globbing): Round 125: readFile (binary/non-UTF8 content — garbled, not null): Round 125: output() (undefined/NaN/Infinity — typeof checks and JSON.stringify): === Test Results === [Utils] replaceInFile failed for /var/folders/ck/y983js7n5lz1z5ls6fq8v0cm0000gn/T/utils-test-readonly-1773115926290/readonly.txt: EACCES: permission denied, open '/var/folders/ck/y983js7n5lz1z5ls6fq8v0cm0000gn/T/utils-test-readonly-1773115926290/readonly.txt' ━━━ Running lib/package-manager.test.js ━━━ === Testing package-manager.js === PACKAGE_MANAGERS Constant: detectFromLockFile: detectFromPackageJson: getAvailablePackageManagers: getPackageManager: getRunCommand: getExecCommand: getCommandPattern: getSelectionPrompt: setProjectPackageManager: setPreferredPackageManager: detectFromPackageJson (edge cases): getExecCommand (edge cases): getRunCommand (additional): DETECTION_PRIORITY: getCommandPattern (additional): getPackageManager (robustness): getRunCommand (validation): getExecCommand (validation): getPackageManager (source detection): setPreferredPackageManager (success): getCommandPattern (completeness): getRunCommand (PM-specific formats): getExecCommand (PM-specific formats): getExecCommand (args validation): getCommandPattern (regex escaping): getRunCommand (non-string input): getExecCommand (non-string binary): getCommandPattern (escapeRegex completeness): getPackageManager (global config edge cases): Round 30: getCommandPattern edge cases: setProjectPackageManager (write verification, Round 31): getExecCommand (safe argument edge cases, Round 31): Round 34: getExecCommand non-string args: Round 34: detectFromPackageJson with non-string packageManager: Round 48: detectFromPackageJson (version format edge cases): Round 69: getPackageManager (global-config success): Round 71: setPreferredPackageManager (save failure): Round 72: setProjectPackageManager (save failure): Round 80: getExecCommand (truthy non-string args): Round 86: detectFromPackageJson (empty package.json): Round 91: getCommandPattern (empty action): Round 91: detectFromPackageJson (whitespace-only packageManager): Round 92: detectFromPackageJson (empty string packageManager): Round 94: detectFromPackageJson (scoped package name @scope/pkg@version): Round 94: getPackageManager (empty string CLAUDE_PACKAGE_MANAGER env var): Round 104: detectFromLockFile (null projectDir — throws TypeError): Round 105: getExecCommand (object args — typeof bypass coerces to [object Object]): Round 109: getExecCommand (path traversal in binary — SAFE_NAME_REGEX permits ../ in binary name): Round 108: getRunCommand (path traversal — SAFE_NAME_REGEX permits ../ via allowed / and . chars): Round 111: getExecCommand (newline in args — SAFE_ARGS_REGEX \s matches \n): === Test Results === ━━━ Running lib/session-manager.test.js ━━━ === Testing session-manager.js === parseSessionFilename: parseSessionMetadata: getSessionStats: Session CRUD: getSessionSize: getSessionTitle: getAllSessions: getSessionById: parseSessionMetadata (edge cases): getSessionStats (edge cases): getSessionSize (edge cases): parseSessionFilename (additional edge cases): writeSessionContent: appendSessionContent: deleteSession: sessionExists: getAllSessions (pagination edge cases): getSessionStats (code blocks & special chars): parseSessionFilename (30-day month validation): getSessionStats (path heuristic edge cases): getAllSessions (combined filters): getSessionById (ambiguous prefix): parseSessionMetadata (edge cases): Round 43: getSessionById (default excludes content): Round 54: search filter scope and path utility: Round 66: getSessionById (noIdMatch — date-only match for old format): Round 30: datetime local-time fix: Round 30: parseSessionFilename edge cases: createdTime fallback (Round 33): getSessionStats Windows path heuristic (Round 46): parseSessionMetadata checkbox case sensitivity (Round 46): Round 69: getSessionById (missing sessions directory): Round 78: getSessionStats (actual file path → reads from disk): Round 78: getAllSessions (hasContent field): Round 75: deleteSession (unlink failure in read-only dir): Round 81: getSessionStats(null) (null input): Round 83: getAllSessions (broken symlink — statSync catch): Round 84: getSessionById (broken symlink — statSync catch): Round 88: parseSessionMetadata content lacking Date/Started/Updated fields: Round 89: getAllSessions (subdirectory skip): Round 91: getSessionStats (mixed Windows path separators): Round 92: getSessionStats (Windows UNC path): Round 93: getSessionStats (drive letter without slash — regex boundary): Round 95: getAllSessions (both negative offset and negative limit): Round 96: parseSessionFilename (Feb 30 — impossible date): Round 96: getAllSessions (limit: Infinity — pagination bypass): Round 96: getAllSessions (limit: null — destructuring default bypass): Round 97: getAllSessions (whitespace search — truthy but unmatched): Round 98: getSessionById (null sessionId — crashes at line 297): Round 98: parseSessionFilename (null input — crashes at line 30): Round 99: writeSessionContent (null path — error handling): Round 100: parseSessionMetadata (### in item text — lazy regex truncation): Round 101: getSessionStats (non-string input — type confusion crash): Round 101: appendSessionContent (null path — error handling): Round 102: getSessionStats (Unix nonexistent .tmp path — looksLikePath → null content): Round 102: parseSessionMetadata ([x] items in In Progress — regex skips checked): Round 104: parseSessionMetadata (whitespace-only notes — trim reduces to empty): Round 105: parseSessionMetadata (blank line inside section — regex stops at \n\n): Round 106: getAllSessions (array/object limit coercion — Number([5])→5, Number({})→NaN→50): Round 109: getAllSessions (non-session .tmp files — parseSessionFilename returns null → skip): Round 108: getSessionSize (exact 1024-byte boundary — < means 1024 is KB, 1023 is B): Round 110: parseSessionFilename (year 0000 — Date constructor maps 0→1900): Round 110: parseSessionFilename (uppercase ID — regex [a-z0-9]{8,} rejects [A-Z]): Round 111: parseSessionMetadata (nested Round 112: getSessionStats (newline-in-path heuristic): Round 112: appendSessionContent (read-only file): Round 113: parseSessionFilename (century leap year — 100/400 rules): Round 113: parseSessionMetadata (title with markdown formatting — raw markdown preserved): Round 115: parseSessionMetadata (CRLF line endings — \r\n vs \n in section regexes): Round 117: getSessionSize (B/KB/MB formatting at exact boundary thresholds): Round 117: parseSessionFilename (uppercase short ID — regex [a-z0-9] rejects uppercase): Round 119: parseSessionMetadata ("Context to Load" — code block extraction edge cases): Round 120: parseSessionMetadata ("Notes for Next Session" — extraction edge cases): Round 121: parseSessionMetadata (Started/Last Updated time extraction): Round 122: getSessionById (old format no-id — date-only filename match): Round 123: parseSessionMetadata (CRLF section boundaries — \n\n fails to match \r\n\r\n): Round 124: getAllSessions (invalid date format — strict !== comparison): Round 124: parseSessionMetadata (title regex edge cases — /^#\s+(.+)$/m): Results: Passed: 165, Failed: 0 [SessionManager] Error writing session: ENOENT: no such file or directory, open '/nonexistent/deep/path/session.tmp' ━━━ Running lib/session-aliases.test.js ━━━ === Testing session-aliases.js === loadAliases: setAlias: resolveAlias: listAliases: deleteAlias: renameAlias: updateAliasTitle: resolveSessionAlias: getAliasesForSession: cleanupAliases: listAliases (edge cases): setAlias (edge cases): updateAliasTitle (edge cases): saveAliases (atomic write): cleanupAliases (edge cases): renameAlias (edge cases): getAliasesForSession (edge cases): setAlias (reserved names case sensitivity): listAliases (negative limit): setAlias (undefined title): saveAliases (failure paths, Round 31): renameAlias rollback (Round 33): saveAliases backup/restore (Round 33): Round 39: atomic overwrite: Round 48: rapid sequential saves: Round 56: Windows platform atomic write path: Round 64: loadAliases version/metadata backfill: Round 67: loadAliases (empty 0-byte file): Round 67: resolveSessionAlias (null/falsy input): Round 67: loadAliases (metadata-only backfill, version present): updateAliasTitle save failure (Round 70): Round 72: deleteAlias (save failure): Round 73: cleanupAliases (save failure): Round 73: setAlias (save failure): Round 84: listAliases (NaN date fallback in sort comparator): Round 86: loadAliases (truthy non-object aliases field): Round 90: saveAliases (backup restore double failure): Round 95: renameAlias (self-rename same name): Round 100: cleanupAliases (callback returns 0 — falsy non-boolean coercion): Round 102: setAlias (title=0 — falsy coercion silently converts to null): Round 103: loadAliases (array aliases — typeof bypass): Round 104: resolveSessionAlias (path-traversal input — returned unchanged): Round 107: setAlias (whitespace-only title — truthy string stored as-is, unlike sessionPath which is trim-checked): Round 111: setAlias (128-char alias — exact boundary of > 128 check): Round 112: resolveAlias (Unicode rejection): Round 114: listAliases (non-string search — number triggers TypeError): Round 115: updateAliasTitle (empty string title — stored null, returned ""): Round 116: loadAliases (extra unknown JSON fields — preserved by loose validation): Round 118: renameAlias (same name — "already exists" because data.aliases[newAlias] is truthy): Round 118: setAlias (reserved names — case-insensitive rejection): Round 119: renameAlias (reserved newAlias name — parallel check to setAlias): Round 120: setAlias (max alias length boundary — 128 ok, 129 rejected): Round 121: setAlias (sessionPath validation — null, empty, whitespace, non-string): Round 122: listAliases (limit edge cases — 0/negative/NaN are falsy, return all): Round 125: loadAliases (proto key in JSON — safe, no prototype pollution): Results: Passed: 105, Failed: 0 [Aliases] Error parsing aliases file: Unexpected token N in JSON at position 0 ━━━ Running lib/project-detect.test.js ━━━ === Testing project-detect.js === Rule Definitions: Empty Directory: Python Detection: TypeScript/JavaScript Detection: Go Detection: Rust Detection: Ruby Detection: PHP Detection: Fullstack Detection: Dependency Readers: Elixir Detection: Edge Cases: === Results: 28 passed, 0 failed === ━━━ Running hooks/hooks.test.js ━━━ === Testing Hook Scripts === session-start.js: session-start.js (edge cases): check-console-log.js: session-end.js: pre-compact.js: suggest-compact.js: evaluate-session.js: post-edit-console-warn.js: post-edit-format.js: pre-bash-dev-server-block.js: post-edit-typecheck.js: session-end.js (extractSessionSummary): hooks.json Validation: plugin.json Validation: evaluate-session.js: suggest-compact.js: check-console-log.js (exact pass-through): post-edit-format.js (security & extension tests): post-edit-typecheck.js (security & extension tests): Shell wrapper portability: Round 23: evaluate-session.js (config & nullish coalescing): Round 23: session-end.js (update existing file path): Round 23: pre-compact.js (glob specificity): Round 23: session-end.js (extractSessionSummary edge cases): Round 24: suggest-compact.js (interval fix & fd fallback): Round 24: post-edit-format.js (edge cases): Round 24: post-edit-typecheck.js (edge cases): Round 24: session-start.js (edge cases): Round 25: post-edit-console-warn.js (pass-through fix): Round 25: check-console-log.js (edge cases): Round 29: post-edit-format.js (cwd and exit): Round 29: post-edit-typecheck.js (exit and pass-through): Round 29: post-edit-console-warn.js (extension and exit): Round 29: check-console-log.js (exclusion patterns and exit): Round 29: run-all.js test runner improvements: Round 32: post-edit-typecheck (special character paths): Round 32: check-console-log (edge cases): Round 32: post-edit-console-warn (additional edge cases): Round 32: session-end.js (empty transcript): Round 38: evaluate-session.js (tilde expansion & missing config): Round 41: pre-compact.js (multiple session files): Round 40: session-end.js (newline collapse): Round 44: session-start.js (empty session file): Round 49: post-edit-typecheck.js (extension edge cases): Round 49: session-end.js (conditional summary sections): Round 50: session-start.js (alias reporting): Round 50: pre-compact.js (parallel execution): Round 50: session-start.js (graceful degradation): Round 53: post-edit-console-warn.js (max matches truncation): Round 53: post-edit-format.js (non-existent file): Round 55: session-start.js (maxAge 7-day boundary): Round 55: session-start.js (newest session selection): Round 55: session-end.js (stdin overflow): Round 56: post-edit-typecheck.js (tsconfig in parent directory): Round 56: suggest-compact.js (counter file as directory — fallback path): Round 59: session-start.js (unreadable session file — readFile returns null): Round 59: check-console-log.js (stdin exceeding 1MB — truncation): Round 59: pre-compact.js (read-only session file — appendFile error): Round 60: session-end.js (replaceInFile returns false — timestamp update warning): Round 60: post-edit-console-warn.js (stdin exceeding 1MB — truncation): Round 60: post-edit-format.js (valid JSON without tool_input key): Round 64: post-edit-typecheck.js (valid JSON without tool_input): Round 66: session-end.js (entry.role user fallback): Round 66: session-end.js (nonexistent transcript path): Round 70: session-end.js (entry.name/entry.input fallback): Round 71: session-start.js (default source — selection prompt): Round 74: session-start.js (main catch — unrecoverable error): Round 75: pre-compact.js (main catch — unrecoverable error): Round 75: session-end.js (main catch — unrecoverable error): Round 76: evaluate-session.js (main catch — unrecoverable error): Round 76: suggest-compact.js (main catch — double-failure): Round 80: session-end.js (entry.message.role user — third OR condition): Round 81: suggest-compact.js (COMPACT_THRESHOLD > 10000): Round 81: session-end.js (user entry with non-string non-array content): Round 82: session-end.js (entry.tool_name without type=tool_use): Round 82: session-end.js (template marker present but regex no-match): Round 87: post-edit-format.js (stdin exceeding 1MB — truncation): Round 87: post-edit-typecheck.js (stdin exceeding 1MB — truncation): Round 89: post-edit-typecheck.js (TypeScript error detection path): Round 89: session-end.js (entry.name + entry.input fallback in extractSessionSummary): Round 90: readStdinJson (timeout fires when stdin stays open): Round 94: session-end.js (tools used without files modified): === Test Results === ━━━ Running hooks/evaluate-session.test.js ━━━ === Testing evaluate-session.js === Threshold boundary (default min=10): Edge cases: Config file parsing: Round 53: CLAUDE_TRANSCRIPT_PATH fallback: Round 65: regex whitespace tolerance around colon: Round 85: config parse error catch block: Round 86: config learned_skills_path override: Results: Passed: 16, Failed: 0 ━━━ Running hooks/suggest-compact.test.js ━━━ === Testing suggest-compact.js === Basic counter functionality: Threshold suggestion: Interval suggestion: Environment variable handling: Corrupted counter file: Session isolation: Exit code: Threshold boundary values: Default session ID fallback (Round 64): Results: Passed: 20, Failed: 0 ━━━ Running integration/hooks.test.js ━━━ === Hook Integration Tests === Hook Input Format Handling: Hook Output Format: Hook Exit Codes: Realistic Scenarios: Session End Transcript Parsing: Error Handling: Round 51: Timeout Enforcement: Round 51: hooks.json Schema Validation: === Test Results === ━━━ Running ci/validators.test.js ━━━ === Testing CI Validators === validate-agents.js: validate-hooks.js: validate-skills.js: validate-commands.js: validate-rules.js: validate-hooks.js (whitespace edge cases): validate-agents.js (whitespace edge cases): validate-commands.js (additional edge cases): validate-hooks.js (schema edge cases): validate-hooks.js (legacy format errors): validate-agents.js (empty directory): validate-commands.js (whitespace edge cases): validate-rules.js (mixed files): validate-hooks.js (Round 27 edge cases): validate-commands.js (Round 27 edge cases): validate-skills.js (mixed dirs): Round 30: validate-commands (skill warnings): Round 30: validate-agents (model validation): Round 32: validate-agents (empty frontmatter): Round 32: validate-rules (non-file entries): Round 32: validate-commands (agent reference with valid workflow): Round 42: validate-agents (case sensitivity): Round 42: validate-commands (missing agents dir): Round 42: validate-hooks (empty matchers array): Round 47: validate-hooks (inline JS escape sequences): Round 47: validate-agents (frontmatter lines without colon): Round 52: validate-commands (inline backtick refs): Round 52: validate-commands (workflow whitespace): Round 52: validate-rules (code-only content): Round 57: validate-skills.js (SKILL.md is a directory — readFileSync error): Round 57: validate-rules.js (broken symlink — statSync catch block): Round 57: validate-commands.js (adjacent code blocks both stripped): Round 58: validate-agents.js (unreadable agent file — readFileSync catch): Round 58: validate-agents.js (frontmatter line with colon at position 0): Round 58: validate-hooks.js (command is a plain object — not string or array): Round 63: validate-hooks.js (object-format matcher missing matcher field): Round 63: validate-commands.js (unreadable command file): Round 63: validate-commands.js (empty commands directory): Round 65: validate-rules.js (empty directory — no .md files): Round 65: validate-skills.js (empty directory — no subdirectories): Round 70: validate-commands.js (would create: skip): Round 72: validate-hooks.js (async and timeout type validation): Round 73: validate-commands.js (unreadable skill entry — statSync catch): Round 76: validate-hooks.js (invalid JSON in hooks.json): Round 78: validate-hooks.js (wrapped hooks format): Round 79: validate-commands.js (warnings count in output): Round 80: validate-hooks.js (legacy array format): Round 82: validate-hooks (Notification and SubagentStop event types): Round 83: validate-agents (whitespace-only frontmatter field value): Round 83: validate-skills (empty SKILL.md file): Results: Passed: 136, Failed: 0 ━━━ Running scripts/claw.test.js ━━━ === Testing claw.js === Storage: Context: Delegation: REPL/Meta: NanoClaw v2: Results: Passed: 19, Failed: 0 ━━━ Running scripts/setup-package-manager.test.js ━━━ === Testing setup-package-manager.js === --help: --detect: --list: --global: --project: positional argument: environment variable: --detect output completeness: --global flag validation (Round 31): --project flag validation (Round 31): --detect marker uniqueness (Round 45): --list output completeness (Round 45): --global success path (Round 62): bare PM name success (Round 62): --detect source label (Round 62): --project success path (Round 68): --list (current) marker (Round 68): Round 74: setGlobal catch (save failure): Round 74: setProject catch (save failure): Results: Passed: 31, Failed: 0 ━━━ Running scripts/skill-create-output.test.js ━━━ === Testing skill-create-output.js === SkillCreateOutput constructor: header(): analysisResults(): patterns(): instincts(): output(): nextSteps(): footer(): progressBar edge cases: empty array edge cases: box() crash prevention: box() alignment: box() content overflow: header() width alignment (Round 34): box() width accuracy (Round 35): analysisResults zero values (Round 54): demo export (Round 68): Round 85: patterns() confidence=0 nullish coalescing: Round 87: analyzePhase() async method: Results: Passed: 36, Failed: 0 ╔══════════════════════════════════════════════════════════╗ |
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
affaan-m
left a comment
There was a problem hiding this comment.
Automated review: checks are failing. Please fix failures before review.
Description
Summary\n- let post-edit-format treat formatter config files as project-root markers, not just package.json\n- add broader Prettier config variants to the shared detection list\n- add a hook test covering config-only repos with nested files\n\nCloses #362.\n
Validation:
Type of Change
fix:Bug fixfeat:New featurerefactor:Code refactoringdocs:Documentationtest:Testschore:Maintenance/toolingci:CI/CD changesChecklist
node tests/run-all.js)